
/**********************************************************
 *              BYPASS MODE _ DO NOTHING                  *
 **********************************************************/
void bypass() {
    LEDS_OFF();
    message_out(0, 15);  //BYPASS message
    message_out(1,0);
  while (digitalRead(BYPASS) == HIGH) {
  }
  message_reset = true;  //force a new message on exit
  last_mode = 2;    //force a new interpolation on exit
}

/**********************************************************
 *                    ALL LED's OFF                       *
 **********************************************************/
void LEDS_OFF() {
    mySerial.print("?H5");     //turn off all RANGE LED's (auxiliary pins on display LCD)
    mySerial.print("?H4");   
    mySerial.print("?H6");
    digitalWrite(tapLED, HIGH);
}


/**********************************************************
 *                    Delay Setting test                  *
 **********************************************************/
void get_delay_mode() {
  ping_count = 0; //quality check for ISR
  for (int i = 15; i < 17; i++) {  //go through the voltages and read 
    write_DAC_A (time_array[i][0]);  //set control voltage
    analogWrite(ping_out_Pin, 127);  //output a nice square wave pulse with a 50% duty cycle
    time_stamp = millis();  
    listen = true;   //set a flag to wait till ISR does its thing
    delay(70); //put out a pulse - about the shortest and still get a few cycles in there
    analogWrite(ping_out_Pin, 0);

    seg_start = millis();  //set watchdog imer
    while (listen == true) {  //wait for ISR to complete **note the ISR will set this false before the 50 msec delay is done on short delays
      if ((millis() - seg_start) > 1800) {
        break;    //lost the echo - terminate the clibration routine, back all the way out
      }  
    }
    time_array[i][1] = delay_time;   //populate the delay time in the array use as scratchpad - will be overwitten
    if ((millis() - seg_start) > 1800)    //keep backing out if there was a problem
      break;
    delay(300); //make sure echos are clear   
  }
  write_DAC_A (0);   //we are done the calibration or it crapped out - reset the voltage to zero
  if ((millis() - seg_start) > 1800)   {
    //    Serial.println("Timeout Error - No Signal");  //crapped out
    message_out(0, 8);     //Timeout
    message_out(1, 2);     //increase GAIN
    reset_message(3000, true);

  }
  else {     //we have a signal, lets check some samples
    if (ping_count != 2) {    //if calibration completes, just check on samples read
      //fails at this stage, get out
      calibration = false;  //done test for this round
      message_out(0, 1);     //decrease feedback
      message_out(1, 2);     //increase gain
      reset_message(3000, true);
    }
    //      else 
    //graceful exit
    //this sets which colums to use when operating on the time arrays and checking values
    else {
      int average = 0;
      for (int i = 15; i < 17; i++)   //debugging
        average =  average + time_array[i][1];
      average = average / 2;
      if (average > 800) {
        SHORT_setting = LOW;
        y = 1;               //use the third column in the time array data for time values

        message_out(1, 21);     //Long mode


      }
      else {
        SHORT_setting = HIGH;
        y = 0;               //use the second column in the time array data for time values

        message_out(1, 20);     //Short mode

      }
      calib();   //call next function if we succeed 
    }
  }
}




/**********************************************************
 *        Delay setting calibration function          *
 **********************************************************/
void calib() {
  ping_count = 0; //quality check for ISR
  for (int i = 0; i < 17; i++) {  //go through the voltages and measure the time delays
    write_DAC_A (time_array[i][0]);  //set control voltage

    //send the ping out and begin timing
    analogWrite(ping_out_Pin, 127);  //output a nice square wave pulse with a 50% duty cycle
    time_stamp = millis();  
    listen = true;   //set a flag to wait till ISR does its thing
    delay(70); //put out a pulse - about the shortest and still get a few cycles in there
    analogWrite(ping_out_Pin, 0);

    // let the ISR work
    seg_start = millis();  //set watchdog imer
    while (listen == true) {  //wait for ISR to complete **note the ISR will set this false before the 50 msec delay is done on short delays
      if ((millis() - seg_start) > 1800) {
        break;    //lost the echo - terminate the clibration routine, back all the way out
      }  
    }

    // ping is recorded 
    time_array[i][1+y] = delay_time;   //populate the delay time in the array
    if ((millis() - seg_start) > 1800)    //keep backing out if there was a problem
      break;
    delay(100); //make sure echos are clear   
  }

  //we are done the calibration or it crapped out
  write_DAC_A (0);   // reset the voltage to zero
  if ((millis() - seg_start) > 1800) {
    message_out(0, 8);     //Timeout
    message_out(1, 2);     //increase GAIN
    reset_message(3000, true);

  }
  else {
    if (ping_count != 17)  { //if calibration completes, just check on samples read
      calibration = false;  //done test for this round
      message_out(0, 1);     //decrease feedback
      message_out(1, 2);     //increase gain
      reset_message(3000, true);
    }
    else {
      check_for_feedback () ;
      message_out(0, (22+y));           //message that MODE IS CALIBRATED
      message_out(1, 0);
      YES_calib_msg_Short = true;    //just so the message shows up with some delay first time calibrated
      YES_calib_msg_Long = true;
      reset_message(2000, false);
      //Short mode or long mode message from main loop
    }
  }
  calibration = false;  //done calibration 
}

/**********************************************************   //feedback on the delay will cause multiple echoes and non linear progression
 *                   Check For Feedback                   *
 **********************************************************/

void check_for_feedback () {

  for (int x = 0; x < 16; x++) {

    if ((time_array[x+1][1+y] - time_array[x][1+y]) <= -2) {
      message_out(0, 1);     //decrease feedback
      message_out(1, 0);     //increase gain
      reset_message(2000, true);
    }
    break;
  }
  calib_fail = false;
}

/**********************************************************   //feedback on the delay will cause multiple echoes and non linear progression
 *         Get position of max / min time values          *
 **********************************************************/// could also use this function to find the position for the highest and lowest numbers

void check_for_maxmin(byte y) {

  max_min_val[0][y] = time_array[0][1+y];   //minimum measured time value in short(fast) mode
  max_min_val[1][y] = time_array[16][1+y];  //maximum measured time value in short(fast) mode

  max_min_pos[0][y] = 0;   //position of minimum measured time value in short(fast) mode
  max_min_pos[1][y] = 16;  //position of maximum measured time value in short(fast) mode

  for (int x = 1; x < 5; x++) {    //go through the first few data elements to eliminate flat parts of the line //volt vs time//

    if (time_array[x][1+y] <= (max_min_val[0][y] + 8)) { //add 8 so there is meaningful seperation between samples 
      max_min_val[0][y] = time_array[x][1+y];
      max_min_pos[0][y] = x;
    }
  }
  for (int x = 15; x > 5; x--) {    //go through the last few data elements to eliminate flat parts of the line //volt vs time//

    if ((8 + time_array[x][1+y]) >= max_min_val[1][y]) {  //add 8 so there is meaningful seperation between samples 
      max_min_val[1][y] = time_array[x][1+y];
      max_min_pos[1][y] = x;
    }
  }
}
//*****
void check_for_maxmin_error(byte y) {    //split thsi soff so we don't get odd messages when we load from EEPROM - only shows up on calibrate
  if (max_min_pos[1][y] < 13) {
    //    Serial.println("Reduce TIME setting");    //we are pegged at the high end (or the bottom - not much u can do).... not many useful samples 
    message_out(0, 7);     //decrease TIME
    message_out(1, 3);     //recalibrate
    reset_message(2000, false);
  }
//  Serial.print ("Position");   //output for debug
//  Serial.print ("\t");
//  Serial.println ("Value");
//
//  //
//  Serial.print (max_min_pos[0][y]);
//  Serial.print ("\t \t");
//  Serial.println (max_min_val[0][y]);
//  //
//  Serial.print (max_min_pos[1][y]);
//  Serial.print ("\t \t");
//  Serial.println (max_min_val[1][y]);
}


/**********************************************************
 *                reset message timout                     *
 **********************************************************/

void reset_message (int timeout_val, boolean reset_array) {
  if (reset_array == true)  {  
    time_array[0][y+1] = -1;  //set the time array to uncalibrated so we don't use these values
    calib_fail = true;        //caibration failed, do not operate on the arrays
  }
  message_reset_time = millis() + timeout_val;

  message_reset = true;
  //    NO_calib_msg_Short = false;  //set these in main loop after message_reset_time times out
  //    NO_calib_msg_Long = false;
  //    YES_calib_msg_Short = false;
  //    YES_calib_msg_Long = false;
}


/**********************************************************
 *  Debugging output array  -  print to serial            *
 **********************************************************/
void output_array() {
  Serial.println ("");
  Serial.print ("Data");
  Serial.print ("\t \t");
  Serial.println ("Delay");


  for (int x=0; x<17; x++) {

    Serial.print (time_array[x][0]);
    Serial.print ("\t \t");
    Serial.println (time_array[x][1 + y]);

  }
}


/**********************************************************
 *           Straight Line Interpolation                  *
 **********************************************************/
unsigned long DAC_value = 0;

void interpolate () {

  //  m_sec = millis_tap_tempo * Subdivision;   //this is now in loop so we can test for changes in state 


    if (m_sec < max_min_val[0][y])  {       //not in range but zero out the DAC as a graceful failure
    mySerial.print("?H5");     //light up TOO LOW LED
    mySerial.print("?L4");   
    mySerial.print("?H6");
    write_DAC_A (0);                       
  }
  else if (m_sec > max_min_val[1][y])  {          //not in range but max out the DAC as a graceful failure
    mySerial.print("?H5");     //light up TOO HIGH LED
    mySerial.print("?H4");   
    mySerial.print("?L6");
    write_DAC_A (4095);
  }

  else {                                                                 // delay value is OK, perform interpolation
    for (int x = max_min_pos[0][y]; x <= max_min_pos[1][y]; x++) {     //set the max min positions for linear part of the data
      if (time_array[x][y+1] <= m_sec && m_sec <= time_array[x+1][y+1]) {   //the big one

        DAC_value = (((m_sec - time_array[x][y+1])*(time_array[x+1][0] - time_array[x][0]))/(time_array[x+1][y+1] - time_array[x][y+1])) + time_array[x][0] ; 
        break;  
      } 
    }
    write_DAC_A (DAC_value);  //in range
    mySerial.print("?L5");     //light up IN RANGE LED
    mySerial.print("?H4");   
    mySerial.print("?H6");   

  }

}

/**********************************************************
 *             SUBDIVISION for TAP TEMPO                  *  
 **********************************************************/
void subdivide_tap () {
  int Pot_Val_A = analogRead(POT_A);  
  if (Pot_Val_A < 64) 
    Subdivision = 6;            
  else if(Pot_Val_A < 192)
    Subdivision = 9; 
  else if(Pot_Val_A < 320)
    Subdivision = 12;  
  else if(Pot_Val_A < 448)
    Subdivision = 18;   
  else if(Pot_Val_A < 576)
    Subdivision = 24;   
  else if(Pot_Val_A < 704)
    Subdivision = 36;   
  else if(Pot_Val_A < 832)
    Subdivision = 48;   
  else if(Pot_Val_A < 960)
    Subdivision = 72; 
  else 
    Subdivision = 96;   
}

/**********************************************************
 *                      MIDI SYNC                         *
 **********************************************************/

byte midi_ticks = 0;

void Sync() {      

  midi_ticks = midi_ticks++;  //increment the two output channels

  if (midi_ticks == 24) {    //reached the # of ticks
    tap ();
    midi_ticks = 0;
  }
}

/**********************************************************
 *                      TAP TEMPO                         *
 **********************************************************/
int footswitch_count = 0;

void tap_tempo () {

  tapDelta = ((currentTimer[0] + currentTimer[1])/2)*.02; //set a percentage of tap time to increase or decrease
  // Serial.println (tapDelta);

  int slowState = digitalRead(A_SW); //read the tap footswitch
  int fastState = digitalRead(B_SW);

  if (slowState == HIGH && fastState == HIGH)  //nothing pressed
    footswitch_count = 0;

  else if (footswitch_count < 3) // at least one switch is pressed, as first if statement failed
    footswitch_count++;

  else if (footswitch_count == 3) // at least one switch pressed and waied arbitrary time to take care of debounce time differences - we have a steady state of both inputs
  { 
    footswitch_count++;  //disqualifies any further tests untill switch is released
    if( slowState == LOW && fastState == HIGH) // if slower button (A_SW) pressed, call function to oncrease count of tap registers || slow DOWN ||
    {
      slowerTap(); /* we got a HIGH-LOW transitionon only the A switch, call slowerTap function */
      disp_BPM();
    }

    // if faster button pressed (B_SW), call function to decrease count of tap registers || speed UP || 
    else if( fastState == LOW && slowState == HIGH )
    {
      fasterTap(); /* we got a HIGH-LOW transition only in the B switch, call our fasterTap() function */
      disp_BPM();
    }

    else  
    {
      tap(); /* last possible combination is a HIGH-LOW transition for both, call our tap() function */
      // disp_BPM();
    }
  }  
//************previous tap tempo didn't work with two channels of hardware debounce if there was a slight difference in decay... 
  //  // if slower button (A_SW) pressed, call function to oncrease count of tap registers || slow DOWN ||
  //  if( slowState == LOW && slowState != lastslowState && fastState == HIGH)
  //  {
  //    slowerTap(); /* we got a HIGH-LOW transitionon only the A switch, call slowerTap function */
  //    disp_BPM();
  //  }
  //
  //
  //  // if faster button pressed (B_SW), call function to decrease count of tap registers || speed UP || 
  //  if( fastState == LOW && fastState != lastfastState && slowState == HIGH )
  //  {
  //    fasterTap(); /* we got a HIGH-LOW transition, call our tap() function */
  //    disp_BPM();
  //  }
  //
  //
  //  if(  slowState == LOW && fastState == LOW && slowState != lastslowState )  
  //  {
  //    tap(); /* we got a HIGH-LOW transition, call our tap() function */
  //    // disp_BPM();
  //  }

//  lastfastState = fastState; /* keep track of the state */
//  lastslowState = slowState; /* keep track of the state */

  /* check for timer timeout */
  if( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + 50;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }

  //  /* display the button state on LED 2 */
  //  digitalWrite( 3, tapState );

  /* display the tap blink on LED 13 */

  if( millis() < indicatorTimeout ) {
    digitalWrite( tapLED, LOW );
  }
  else {
    digitalWrite( tapLED, HIGH );
  }
}


unsigned long ctr;  //make this long so it doesn't wrap around too ofetn and miss an update to BPM display
unsigned long lastTap = 0; /* when the last tap happened */
void tap()
{
  /* we keep two of these around to average together later */

 //if ((millis() - lastTap) > 150)  //check for garbage bounces
  //{                                 commneted out, debounce taken care of in previous call 
    currentTimer[1] = currentTimer[0];
    currentTimer[0] = millis() - lastTap;
    lastTap = millis();
    timeoutTime = 0; /* force the trigger to happen immediately - sync and blink! */
    if (currentTimer[0] > (currentTimer[1] + 800))
    {
      ctr = 0;
      currentTimer[0] = currentTimer[1]; //keep giant garbage numbers out
      disp_millis_tap_tempo = 0;   //force a refresh to test display BPM below
      disp_TAP(); 
     message_reset = false; // so a previous message does not overwrite until disp_BPM follows
    }
    else 
    {
      ctr++;  
    }
    if (ctr > 1)   // after 3 taps - first resets 0 and 1 follow - display only the smaller of the milliseconds measured, (higher BPM) or if the BMPis ging down, has to be above a msec threshold 
    {              
      if ( disp_millis_tap_tempo > ((currentTimer[0] + currentTimer[1]) / 2) || (disp_millis_tap_tempo +4) < ((currentTimer[0] + currentTimer[1]) / 2))  // MIDI creates a constant stream of taps
        disp_BPM();  
    } 
 // }
}
void rescheduleTimer()
{
  /* set the timer to go off again when the time reaches the 
   timeout.  The timeout is all of the "currentTimer" values averaged
   together, then added onto the current time.  When that time has been
   reached, the next tick will happen...
   */
  timeoutTime = millis() + ((currentTimer[0] + currentTimer[1])/2);
}

void slowerTap()
{
  /* function to increase time interval of tap by constant tapDelta 
   */
  for (int i = 0; i < 2; i++) {
    currentTimer[i] = currentTimer[i] + tapDelta;
  }
}

void fasterTap()
{
  /* function to decrease time interval of tap by constant tapDelta 
   */
  for (int i = 0; i < 2; i++) {
    currentTimer[i] = currentTimer[i] - tapDelta;
  }
}

void disp_BPM()
{
  BPM = 120000/(currentTimer[0] + currentTimer[1]);
  millis_tap_tempo = (currentTimer[0] + currentTimer[1]) / 2;  //take average for display of milliseconds
  disp_millis_tap_tempo = millis_tap_tempo;
  if (play_flag == 1) {              //midi clock ticks are present
    mySerial.print("?x00"); 
    mySerial.print("?y0?l");
    mySerial.print(BPM);  
    mySerial.print(" MIDI ");    // cursor to first character of line 0  
    mySerial.print (disp_millis_tap_tempo);
    mySerial.print(" ms");
  }

  else {                              //no midi clock ticks, this is fom a switch input
    mySerial.print("?x00"); 
    mySerial.print("?y0?l");
    mySerial.print(BPM);  
    mySerial.print(" BPM ");    // cursor to first character of line 0  
    mySerial.print (disp_millis_tap_tempo);
    mySerial.print(" msec");
    reset_message(3000, false);   
  }

  millis_tap_tempo = (100*(currentTimer[0] + currentTimer[1])) / 48;  //divide by 24 for multiplication by subdiv. value

}


void disp_TAP()
{
  //  Serial.println ("Reading Tap");
  message_out(0, 13);   //message is displayed until tap value is calculated
  //  reset_message(3000, false);  
}

/**********************************************************
 *             Message Handler for LCD                    *   
 **********************************************************/

//  "?l",               //0 - blank line
//  "Reduce FEEDBACK",  //1
//  "Increase GAIN",    //2
//  "  Re-Calibrate",    //3
//  "Delay Range",      //4
//  " CALIBRATE for",   //5
//  "<<Calibrating>>",  //6
//  "  Reduce TIME",    //7
//  "Signal TIMEOUT",   //8
//  "    Detected",     //9
//  "Delay too SHORT",  //10
//  "Delay too LONG",   //11
//  " ",                //12
//  " ",                //13
//  " ",                //14
//  " ",                //15
//  " ",                //16
//  " ",                //17
//  " ",                //18
//  " ",                //19
//  
//// can use these with odd/even commands by adding "y" to array pointer
//
//  "SHORT Delay Mode",  //20
//  "LONG Delay Mode",   //21
//  "SHORT Calibrated",  //22
//  "LONG Calibrated",   //23
//  "Start SHORT Cal",   //24
//  "Start LONG Cal",    //25

char message_out (byte line, byte phrase) {

  mySerial.print("?x00"); 
  mySerial.print("?y");
  mySerial.print(line, DEC);    // cursor to first character of line 0
  mySerial.print("?l");  
  mySerial.print(messages[phrase]);              // cursor to first character of line 1
}

/**********************************************************
 *    Interrupt service routine on falling edge of input   *   
 **********************************************************/

void ping() {

  if (calibration == true) {   //make sure we are in a valid calibration state
    delay_time = millis() - time_stamp; //may need to scale this somewhat to reflect actual measured timing
    ping_count++  ;   //quality check: ensure only the exact number of measurements were taken
    listen = false;               //pass a flag back to main loop that we are done
  }
}


/**********************************************************
 *             read and write to EEPROM                       *
 **********************************************************/

//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROM_Write(int p_address, int p_value)
	{
	byte lowByte = ((p_value >> 0) & 0xFF);
	byte highByte = ((p_value >> 8) & 0xFF);

	EEPROM.write(p_address, lowByte);
	EEPROM.write(p_address + 1, highByte);
	}

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
int EEPROM_Read(int p_address)
	{
	byte lowByte = EEPROM.read(p_address);
	byte highByte = EEPROM.read(p_address + 1);

  	return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
	}

/**********************************************************
 *             update  DAC from BPM                       *
 **********************************************************/
   void Update_DAC(byte mode) {
   
    tap_tempo ();               //put tap tempo under valid machine state as we need a valid calibration first
    subdivide_tap ();           //still need subdivide_tap in all cases 
    msec_old = m_sec;
    m_sec = (millis_tap_tempo * Subdivision) / 100;  //the 100 multiplier is just to get a few more sig digits
    if (msec_old != m_sec || last_mode != mode)   //check for change in BPM, subdivision, or SHORT / LONG switch position
      interpolate ();
    last_mode = mode; 
   }
/**********************************************************
 *                   DAC output functions                  *
 **********************************************************/
void write_DAC_A(int voltage) {

  // divide the 16 bit word into 8 bit pieces and add 4 control bits on MSB
  dacSPI0 = voltage >> 8;
  dacSPI0  &= 0x0f;  //insurance
  dacSPI0 |= 0x30;   //control bits, channel A, no buffer input, unity gain, no shutdown - 
  dacSPI1 = voltage;
  write_SPI();
}

void write_DAC_B(int voltage) {

  // divide the 16 bit word into 8 bit pieces and add 4 control bits on MSB
  dacSPI0 = voltage >> 8;
  dacSPI0  &= 0x0f;  //insurance
  dacSPI0 |= 0xB0;   //control bits, channel B, no buffer input, unity gain, no shutdown - 
  dacSPI1 = voltage;
  write_SPI();
}

void write_SPI() {
  // transmit value out the SPI port
  digitalWrite(SLAVESELECT,LOW); //enable device 
  SPDR = dacSPI0;
  while (!(SPSR & (1<<SPIF)));
  SPDR = dacSPI1;
  while (!(SPSR & (1<<SPIF)));
  digitalWrite(SLAVESELECT,HIGH); //disable device 
}




























